Skip to content

Guard the deferred local queue and depth counter - #11284

Merged
thebentern merged 5 commits into
developfrom
security-block-c
Jul 30, 2026
Merged

Guard the deferred local queue and depth counter#11284
thebentern merged 5 commits into
developfrom
security-block-c

Conversation

@caveman99

@caveman99 caveman99 commented Jul 29, 2026

Copy link
Copy Markdown
Member

handleDepth and the deferred local ring were plain uint8_t read-modify-written from the loop task and, on nRF52, the Bluefruit BLE task. A lost decrement wedges the counter above zero and silently drops all local loopback until reboot; a lost count update can dispatch and release a slot twice.

Both are now guarded by a lock owned by Router, which is constructed at runtime (router = new ReliableRouter()), so the semaphore is created after the scheduler is up, same as the existing cryptLock. No lock is held across dispatchReceived(), the deferred lock and the pool are never held at the same time, and no path acquires the lock twice.

The static packet pool has the same class of race but is deliberately left out of this PR: those pools are file-scope statics, so adding a lock member would create four binary semaphores before main(), and concurrency::Lock aborts if creation fails. It would also break the non-blocking alloc(0) contract, since Lock::lock() waits portMAX_DELAY.

Summary by CodeRabbit

  • Bug Fixes
    • Fail fast on unsupported packet payload formats and properly release rejected packets.
    • Strengthened sender signature checks to use the authoritative sender key.
    • Treat encryption failures as hard errors to avoid unintended plaintext behavior.
    • Improved thread-safe local loopback delivery and deferred processing to prevent races and double-handling.
  • Documentation
    • Clarified receive-handler behavior (packet ownership/usage) and updated guidance on deferred-local coordination and concurrency expectations.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ba78b9f-bb57-4a9f-a117-998ce68f0017

📥 Commits

Reviewing files that changed from the base of the PR and between eda1a30 and db01c45.

📒 Files selected for processing (1)
  • src/mesh/Router.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/mesh/Router.h

📝 Walkthrough

Walkthrough

Router updates packet validation, XEdDSA policy verification, encryption-failure handling, and packet-pool cleanup. It also synchronizes nested local delivery and deferred queue draining across concurrent BLE and loop-task paths.

Changes

Router packet handling and deferred delivery

Layer / File(s) Summary
Packet validation, authentication, encryption, and cleanup
src/mesh/Router.cpp
Releases packets for invalid payload variants, uses authoritative public keys for sender verification, broadens the Balanced-mode unsigned-drop condition, and returns PCKI_FAILED when Curve25519 encryption fails.
Deferred delivery synchronization contract
src/mesh/Router.h
Adds deferredLock and documents handle-depth protection, packet ownership, and lock requirements for deferred-local queue operations.
Synchronized deferred delivery and dispatch depth
src/mesh/Router.cpp
Coordinates nested delivery, handle-depth updates, queue draining, dispatch, and packet release with locked state transitions and unlocked processing.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BLETask
  participant LoopTask
  participant Router
  participant DeferredLocalQueue
  BLETask->>Router: handleReceived(packet)
  Router->>Router: Lock deferredLock and increment handleDepth
  LoopTask->>Router: deliverLocal(packet)
  Router->>Router: Lock deferredLock and inspect handleDepth
  Router->>DeferredLocalQueue: Enqueue copied packet when nested
  Router->>DeferredLocalQueue: Dequeue deferred packets while outermost
  Router->>Router: Dispatch queued packets outside deferredLock
Loading

Possibly related PRs

Suggested labels: bug

Suggested reviewers: thebentern

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the bug and fix, but it omits the required template sections, especially the attestations and testing details. Rewrite the PR description using the repository template and add the attestations, including what you tested and any devices you could not verify.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: adding locking around the deferred local queue and depth counter.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch security-block-c

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (31)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Meshnology W10 meshnology_w10 esp32-s3
Meshnology W12 meshnology_w12 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Pocket V3 rak_wismesh_pocket nrf52840
RAK WisMesh Pod rak_wismesh_pod nrf52840
RAK WisMesh Repeater Mini V2 rak_wismesh_repeater_mini nrf52840
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-29. Updated for db01c45.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/mesh/Router.cpp`:
- Around line 1251-1256: Update the deliverLocal final-drain logic around
nested, deferredLock, and handleDepth so the final empty-check and decrement to
zero occur under deferredLock, and only while the deferred ring remains empty.
After allocating the packet copy, re-check handleDepth and append the copy under
the same lock, making the enqueue decision atomic with the final drain check.
Keep dispatchReceived() outside deferredLock.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c2b715cf-b263-4f68-a6db-82466e6e1e89

📥 Commits

Reviewing files that changed from the base of the PR and between 50a37b3 and 2e87743.

📒 Files selected for processing (3)
  • src/mesh/MemoryPool.h
  • src/mesh/Router.cpp
  • src/mesh/Router.h

Comment thread src/mesh/Router.cpp
@caveman99 caveman99 changed the title Synchronize the packet pool and the deferred local queue Guard the deferred local queue and depth counter Jul 29, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/mesh/Router.cpp (1)

1251-1256: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Make handle-depth admission and exit atomic.

Line 1254 releases the lock before dispatch admission, so two tasks can both observe depth zero and concurrently enter dispatchReceived(). The prior final-drain race also remains: an enqueuer can observe nonzero depth, allocate a copy, then append it after Lines 1311-1313 drop depth to zero—stranding it until another receive.

Reserve the outermost handler under deferredLock; make the post-allocation enqueue decision atomic with the final empty-check/decrement. Keep dispatch outside the lock and avoid calling the locking queue helpers while already holding it.

Also applies to: 1285-1314

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mesh/Router.cpp` around lines 1251 - 1256, Make handler-depth admission
and deferred-queue finalization atomic under deferredLock: reserve the outermost
handler while holding the lock before dispatchReceived(), and combine
post-allocation enqueue selection with the final empty-check/decrement so no
item can be stranded. Keep dispatchReceived() outside the lock and avoid locking
queue helpers while deferredLock is held; update the surrounding logic using
nested, handleDepth, and the deferred queue operations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/mesh/Router.cpp`:
- Around line 1251-1256: Make handler-depth admission and deferred-queue
finalization atomic under deferredLock: reserve the outermost handler while
holding the lock before dispatchReceived(), and combine post-allocation enqueue
selection with the final empty-check/decrement so no item can be stranded. Keep
dispatchReceived() outside the lock and avoid locking queue helpers while
deferredLock is held; update the surrounding logic using nested, handleDepth,
and the deferred queue operations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 98bcef02-f21a-4abf-82b7-0b18a5835597

📥 Commits

Reviewing files that changed from the base of the PR and between 2e87743 and d4dc786.

📒 Files selected for processing (2)
  • src/mesh/Router.cpp
  • src/mesh/Router.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/mesh/Router.h

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/mesh/Router.cpp`:
- Around line 1275-1278: Update the fallback branch in handleReceived() so the
copied packet is routed back through handleReceived() rather than calling
dispatchReceived() directly. Preserve packetPool.release(copy) after the
delegated handling, ensuring loopback deliveries remain subject to the existing
depth-based deferral.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 26a075e8-7e75-40d6-8b69-32715c3f7012

📥 Commits

Reviewing files that changed from the base of the PR and between d4dc786 and 05c3255.

📒 Files selected for processing (2)
  • src/mesh/Router.cpp
  • src/mesh/Router.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/mesh/Router.h

Comment thread src/mesh/Router.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/mesh/Router.cpp`:
- Around line 1302-1343: Update Router::handleReceived so the final handleDepth
decrement and determination of whether this frame is the last active frame occur
under the same deferredLock acquisition. If this frame becomes the drain owner,
immediately re-elevate handleDepth to keep it at least 1 throughout
deferred-ring draining, then run the existing drain loop; otherwise return after
the atomic decrement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2be740ce-ed5b-438b-b656-974dfb0eab9c

📥 Commits

Reviewing files that changed from the base of the PR and between cd870fa and 8bd36fa.

📒 Files selected for processing (2)
  • src/mesh/Router.cpp
  • src/mesh/Router.h

Comment thread src/mesh/Router.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens Router’s local loopback deferral path against cross-task races (notably on nRF52 where the BLE task can drive router code concurrently with the main loop task). It introduces a Router-owned lock to make handleDepth and the deferred-local ring updates atomic, preventing lost decrements and double-drains/double-releases that could otherwise wedge loopback delivery or corrupt packet ownership.

Changes:

  • Add deferredLock to guard handleDepth and the deferred local ring bookkeeping.
  • Use concurrency::LockGuard in deliverLocal() and handleReceived() to make depth updates and ring drain decisions atomic while keeping dispatchReceived() outside the lock.
  • Update Router.h documentation around handleReceived() ownership semantics.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/mesh/Router.h Adds a Router-owned lock for deferred-local coordination and updates API/ownership documentation.
src/mesh/Router.cpp Wraps handleDepth and deferred-ring enqueue/dequeue/drain decisions in a lock to avoid race-induced wedges/double handling.

Comment thread src/mesh/Router.h Outdated
@thebentern
thebentern added this pull request to the merge queue Jul 30, 2026
Merged via the queue into develop with commit a5940b4 Jul 30, 2026
101 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Pull request that fixes bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants